import { ref, reactive, onUnmounted } from 'vue'
import service from '@/utils/api'
import { PlainMessage, determineFileType } from '@/utils'
import { URLS } from '@/constants'
import HashWorker from '@/workers/hashWorker.js?worker'
import axios from 'axios'

const UPLOAD_STATUS = {
  PREPARING: 'preparing',
  HASHING: 'hashing',
  UPLOADING: 'uploading',
  SUCCESS: 'success',
  ERROR: 'error',
  PAUSED: 'paused',
  CANCELED: 'canceled'
}

export const useFileUpload = (options = {}) => {
  const defaultOptions = {
    chunkSize: 2 * 1024 * 1024, // 2MB
    threads: navigator.hardwareConcurrency || 4, // 并发上传数，默认使用CPU核心数
    autoRetry: true, // 自动重试
    maxRetryCount: 3 // 最大重试次数
  }

  const config = { ...defaultOptions, ...options }

  // 状态管理
  const uploadMap = reactive(new Map())
  const isUploading = ref(false)
  const pausedUploads = reactive(new Set())

  // 用于存储worker的引用
  let hashWorker = null

  // 切片文件
  const createFileChunks = (file, chunkSize = config.chunkSize) => {
    const chunks = []
    let cur = 0
    while (cur < file.size) {
      chunks.push({
        file: file.slice(cur, cur + chunkSize),
        start: cur,
        end: cur + chunkSize >= file.size ? file.size : cur + chunkSize
      })
      cur += chunkSize
    }
    return chunks
  }

  // 计算文件哈希
  const calculateHash = async file => {
    return new Promise((resolve, reject) => {
      // 创建文件切片用于计算哈希
      const chunks = createFileChunks(file)

      // 创建 web worker
      hashWorker = new HashWorker()

      hashWorker.onmessage = e => {
        const { hash, error } = e.data

        if (error) {
          hashWorker.terminate()
          hashWorker = null
          reject(new Error(error))
          return
        }

        if (hash) {
          hashWorker.terminate()
          hashWorker = null
          resolve(hash)
        }
      }

      hashWorker.onerror = err => {
        reject(err)
        hashWorker.terminate()
        hashWorker = null
      }

      hashWorker.postMessage({
        fileChunks: chunks.map(chunk => chunk.file)
      })
    })
  }

  // 检查文件是否已上传
  const checkFileExists = async fileHash => {
    try {
      const { data } = await service.post(URLS.UPLOAD_CHECK, {
        fileHash
      })

      return data
    } catch (error) {
      PlainMessage.error('文件状态检查失败，请重试')
    }
  }

  // 存储取消令牌的对象
  const chunkCancelTokens = reactive({})

  // 上传单个分片
  const uploadChunk = async ({
    chunk,
    index,
    filename,
    fileId,
    retryCount = 0
  }) => {
    const file = uploadMap.get(fileId)

    if (!file) return

    if (file && pausedUploads.has(file.id)) {
      return Promise.reject('PAUSED')
    }

    if (retryCount > config.maxRetryCount) {
      return Promise.reject(
        new Error(`分片 ${index} 上传失败，已超过最大重试次数`)
      )
    }

    const formData = new FormData()
    formData.append('chunk', chunk.file)
    formData.append('chunkHash', `${file.fileHash}-${index}`)
    formData.append('fileHash', file.fileHash)
    formData.append('filename', filename)
    formData.append('chunkIndex', index)
    formData.append('totalChunks', file.totalChunks)

    const source = axios.CancelToken.source()
    if (!chunkCancelTokens[file.id]) {
      chunkCancelTokens[file.id] = {}
    }
    chunkCancelTokens[file.id][index] = source

    try {
      await service.post(URLS.UPLOAD_CHUNK, formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        cancelToken: source.token,
        onUploadProgress: e => {
          if (!file) return

          const chunkProgress = Math.floor((e.loaded / e.total) * 100)
          file.chunkProgress[index] = chunkProgress

          // 更新总进度
          const totalChunkProgress = Object.values(file.chunkProgress).reduce(
            (acc, val) => acc + val,
            0
          )

          file.progress = Math.floor(
            totalChunkProgress / Object.keys(file.chunkProgress).length
          )
        }
      })

      // 请求成功后删除取消令牌
      if (chunkCancelTokens[file.id]) {
        delete chunkCancelTokens[file.id][index]
      }

      return Promise.resolve({ index })
    } catch (error) {
      if (config.autoRetry) {
        await new Promise(resolve => setTimeout(resolve, 1000))
        return uploadChunk({
          chunk,
          index,
          filename,
          fileId,
          retryCount: retryCount + 1
        })
      }
      return Promise.reject(error)
    }
  }

  // 合并分片
  const mergeChunks = async (file, parentId) => {
    const category = determineFileType(file.name)

    await service.post(URLS.UPLOAD_MERGE, {
      parentId,
      filename: file.name,
      fileHash: file.fileHash,
      totalChunks: file.totalChunks,
      category
    })
  }

  // 上传文件主函数
  const uploadFile = async (rawFile, parentId) => {
    if (!rawFile) return

    const fileId = `file-${Date.now()}-${Math.random().toString(36)}`

    // 添加到上传队列
    uploadMap.set(fileId, {
      id: fileId,
      name: rawFile.name,
      size: rawFile.size,
      progress: 0,
      status: UPLOAD_STATUS.PREPARING,
      rawFile,
      fileHash: '',
      totalChunks: 0,
      chunkProgress: {}
    })

    const fileEntry = uploadMap.get(fileId)
    try {
      // 计算哈希
      fileEntry.status = UPLOAD_STATUS.HASHING
      fileEntry.fileHash = await calculateHash(rawFile)

      const { exists, uploadedChunks = [] } = await checkFileExists(
        fileEntry.fileHash
      )

      if (exists) {
        // 秒传
        fileEntry.status = UPLOAD_STATUS.SUCCESS
        fileEntry.progress = 100
        PlainMessage.success(`${rawFile.name} 秒传成功`)
        return
      }

      // 需要上传，准备分片
      isUploading.value = true
      fileEntry.status = UPLOAD_STATUS.UPLOADING

      const chunks = createFileChunks(rawFile)

      fileEntry.totalChunks = chunks.length

      chunks.forEach((_, index) => {
        fileEntry.chunkProgress[index] = 0
      })

      // 过滤出未上传的分片
      const chunksToUpload = chunks.filter(
        (_, index) => !uploadedChunks.includes(index)
      )

      // 已上传的分片直接标记为100%
      uploadedChunks.forEach(index => {
        fileEntry.chunkProgress[index] = 100
      })

      // 分组并发上传
      const uploadChunksInBatches = async chunks => {
        let allSuccess = true

        for (let i = 0; i < chunks.length; i += config.threads) {
          // 每批次前检查暂停状态
          if (fileEntry && pausedUploads.has(fileEntry.id)) return false

          const batch = chunks.slice(i, i + config.threads)
          try {
            await Promise.all(
              batch.map((chunk, chunkIndex) => {
                const realIndex = i + chunkIndex
                return uploadChunk({
                  chunk,
                  index: realIndex,
                  filename: fileEntry.name,
                  fileId: fileEntry.id
                })
              })
            )
          } catch (error) {
            console.log(error)
            allSuccess = false
          }
        }
        return allSuccess
      }

      const allSuccess = await uploadChunksInBatches(chunksToUpload)

      if (
        !allSuccess &&
        fileEntry.status !== UPLOAD_STATUS.PAUSED &&
        fileEntry.status !== UPLOAD_STATUS.CANCELED
      )
        throw new Error('分片上传失败')

      // 如果状态是暂停则不执行合并操作
      if (
        pausedUploads.has(fileEntry.id) ||
        fileEntry.status === UPLOAD_STATUS.CANCELED
      ) {
        fileEntry.status = UPLOAD_STATUS.PAUSED
        return
      }

      // 合并请求
      await mergeChunks(fileEntry, parentId)

      fileEntry.status = UPLOAD_STATUS.SUCCESS
      fileEntry.progress = 100

      PlainMessage.success(`${fileEntry.name} 上传成功`)
    } catch (error) {
      fileEntry.status = UPLOAD_STATUS.ERROR
      PlainMessage.error(
        `${fileEntry.name} 上传失败: ${error.message || '未知错误'}`
      )
    } finally {
      isUploading.value = false
    }
  }

  const clearUploadRecord = fileId => {
    uploadMap.get(fileId).status = UPLOAD_STATUS.CANCELED
    uploadMap.delete(fileId)
  }

  // 取消上传请求
  const cancelUpload = async file => {
    clearUploadRecord(file.id)
    // 取消所有正在进行的请求
    if (chunkCancelTokens[file.id]) {
      Object.values(chunkCancelTokens[file.id]).forEach(source => {
        source.cancel()
      })
    }

    await service.delete(URLS.CANCEL_UPLOAD, {
      fileHash: file.fileHash
    })
  }

  // 暂停
  const pauseUpload = fileId => {
    const file = uploadMap.get(fileId)
    if (!file) return false

    pausedUploads.add(fileId)
    file.status = UPLOAD_STATUS.PAUSED

    // 取消所有正在进行的请求
    if (chunkCancelTokens[fileId]) {
      Object.values(chunkCancelTokens[fileId]).forEach(source => {
        source.cancel()
      })
    }

    return true
  }

  // 添加恢复上传函数
  const resumeUpload = fileId => {
    const file = uploadMap.get(fileId)
    if (!file) return false
    pausedUploads.delete(fileId)

    // 文件当前状态为暂停或失败状态，需要重新开始上传
    if ([UPLOAD_STATUS.PAUSED, UPLOAD_STATUS.ERROR].includes(file.status)) {
      uploadMap.delete(fileId)
      uploadFile(file.rawFile)
      return true
    }

    return false
  }

  // 清理资源
  onUnmounted(() => {
    if (hashWorker) {
      hashWorker.terminate()
      hashWorker = null
    }
  })

  return {
    uploadMap,
    isUploading,
    uploadFile,
    cancelUpload,
    pauseUpload,
    resumeUpload,
    clearUploadRecord
  }
}
